package com.hd123.rumba.qt.def;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.qt.run.Interpretor;
import com.hd123.rumba.qt.run.QueryThread;
import com.hd123.rumba.util.ObjectUtil;

public class SQLScript {
  public void execute(Connection connection, QueryThread qt) throws BusinessException, IOException,
      SQLException {
    if (script == null || script.trim().length() == 0)
      return;
    List source = breakLine(script);
    List buf = new ArrayList();
    for (int i = 0; i < source.size(); ++i) {
      String s = (String) source.get(i);
      if (s != null && s.length() > 7 && s.substring(0, 7).toLowerCase().equals("include"))
        expand(buf, s.substring(7));
      else
        buf.add(s);
    }
    buf = filterOutComments(buf);

    boolean ignore = false;
    boolean verbose = false;
    StringBuffer command = new StringBuffer();
    for (int i = 0; i < buf.size(); ++i) {
      String s = interpret((String) buf.get(i), qt);
      if ("ignore\n".equalsIgnoreCase(s))
        ignore = true;
      else if ("verbose\n".equalsIgnoreCase(s))
        verbose = true;
      else if (s != null && s.length() > 7 && s.substring(0, 7).toLowerCase().equals("message"))
        prompt(s.substring(7));
      else if ("go\n".equalsIgnoreCase(s)) {
        if (verbose)
          showmessage(command);
        if (ignore) {
          try {
            if (qt.isCancelled())
              return;
            execute(command, qt.getStatement());
          } catch (SQLException e) {
            if (qt.isCancelled())
              return;
          }
        } else {
          if (qt.isCancelled())
            return;
          execute(command, qt.getStatement());
        }
        command = new StringBuffer();
        ignore = false;
        verbose = false;
      } else {
        command.append(s);
      }
    }
    // 没有最后go的命令块
    if (command.toString().trim().length() > 0) {
      if (verbose)
        showmessage(command);
      if (ignore) {
        try {
          if (qt.isCancelled())
            return;
          execute(command, qt.getStatement());
        } catch (SQLException e) {
          if (qt.isCancelled())
            return;
        }
      } else {
        if (qt.isCancelled())
          return;
        execute(command, qt.getStatement());
      }
    }
  }

  private void execute(StringBuffer command, Statement statement) throws SQLException {
    statement.clearBatch();
    String cmds = command.toString();
    if (ObjectUtil.getDbType(statement.getConnection()) == ObjectUtil.dbtypeMySql) {
      int ib = 0;
      int ie = cmds.indexOf(";\n", ib);
      while (ib < cmds.length() && ie >= 0) {
        String cmd = cmds.substring(ib, ie + 1);
        logger.debug(cmd);
        statement.addBatch(cmd);
        ib = ie + 2;
        ie = cmds.indexOf(";\n", ib);
      }
    } else {
      statement.addBatch(cmds);
    }
    statement.executeBatch();
  }

  private List breakLine(String s) throws IOException {
    List result = new ArrayList();
    LineNumberReader reader = new LineNumberReader(new StringReader(s));
    for (String line = reader.readLine(); line != null; line = reader.readLine())
      result.add(line + "\n");
    return result;
  }

  private List filterOutComments(List in) throws IOException {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < in.size(); ++i)
      sb.append((String) in.get(i));
    return breakLine(filterOutComments(sb.toString()));
  }

  private String filterOutComments(String in) {
    final int NORMAL = 0;
    final int BLOCK = 1;
    final int LINE = 2;
    StringBuffer sb = new StringBuffer();
    int state = NORMAL;
    for (int i = 0; i < in.length();) {
      String s = in.substring(i, i + 1);
      String s2 = null;
      if (i < in.length() - 1)
        s2 = in.substring(i + 1, i + 2);
      switch (state) {
      case NORMAL:
        if ("/".equals(s) && "*".equals(s2)) {
          state = BLOCK;
          i += 2;
        } else if (("-".equals(s) && "-".equals(s2)) || (i == 0 && "#".equals(s))) {
          state = LINE;
          i += 2;
        } else {
          sb.append(s);
          i += 1;
        }
        break;
      case BLOCK:
        if ("*".equals(s) && "/".equals(s2)) {
          state = NORMAL;
          i += 2;
        } else {
          i += 1;
        }
        break;
      case LINE:
        if ("\r".endsWith(s) || "\n".equals(s)) {
          state = NORMAL;
          sb.append(s);
          i += 1;
        } else {
          i += 1;
        }
      }
    }
    return sb.toString();
  }

  private String interpret(String s, QueryThread qt) throws BusinessException, SQLException {
    Interpretor interpretor = Interpretor.getInterpretor(this.getQuery().getSessionValues());
    int state, i, x, y;
    StringBuffer buff = new StringBuffer();
    StringBuffer result = new StringBuffer();
    state = 0;
    x = -1;
    for (i = 0; i < s.length(); ++i) {
      switch (state) {
      case 0:
        if (s.charAt(i) == '\\') {
          state = 1;
        } else {
          result.append(s.substring(i, i + 1));
        }
        break;
      case 1:
        if (s.charAt(i) == '(') {
          state = 2;
          buff = new StringBuffer();
        } else {
          result.append(s.substring(i, i + 1));
          state = 0;
        }
        break;
      case 2:
        if (s.charAt(i) == ',') {
          state = 3;
          x = Integer.parseInt(buff.toString().trim());
          buff = new StringBuffer();
        } else if (s.charAt(i) == ')') {
          state = 0;
          if (buff.toString().equalsIgnoreCase("filename"))
            result.append(query.getName());
          else {
            try {
              result.append(interpretor.interpret("\\(" + buff.toString() + ")"));
            } catch (BusinessException e) {
              result.append(buff.toString());
            }
          }
          buff = new StringBuffer();
        } else {
          buff.append(s.substring(i, i + 1));
          // result.append("<");
          // result.append(buff);
          // result.append(">");
        }
        break;
      case 3:
        if (s.charAt(i) == ')') {
          state = 0;
          y = Integer.parseInt(buff.toString().trim());
          buff = new StringBuffer();
          String cellContent = null;
          try {
            cellContent = query.getSg()[y][x - 1];
          } catch (Exception e) {
            throw new SQLException(e.getClass().getName() + "-" + e.getMessage() + ": " + s);
          }
          if (cellContent == null || "null".equalsIgnoreCase(cellContent))
            cellContent = "";
          result.append(cellContent);// [y][x];
        } else
          buff.append(s.substring(i, i + 1));
        break;
      default:
        buff.append(s.substring(i, i + 1));
      }
      // result.append("<");
      // result.append(query.getName());
      // result.append(">");
    }
    return result.toString();
  }

  private void prompt(String s) {
    // logger.debug(s);
  }

  private void showmessage(StringBuffer sb) {
    // logger.debug(sb.toString());
  }

  private void expand(List buf, String fileName) throws IOException {
    File qFile = query.getFile();
    File iFile = new File(fileName);
    File file = new File(qFile.getParent(), iFile.getName());
    LineNumberReader reader = new LineNumberReader(new FileReader(file));
    for (String s = reader.readLine(); s != null; s = reader.readLine()) {
      if (s.length() > 7 && s.substring(0, 7).equalsIgnoreCase("include"))
        expand(buf, s.substring(8));
      else
        buf.add(s + "\n");
    }
    reader.close();
  }

  public String debugInfo() {
    StringBuffer sb = new StringBuffer();
    sb.append(getClass().getName() + "\n");
    sb.append(getScript() + "\n");
    return sb.toString();
  }

  private QueryDefinition query;
  private String script;
  private static Logger logger = Logger.getLogger(SQLScript.class);

  public QueryDefinition getQuery() {
    return query;
  }

  public void setQuery(QueryDefinition query) {
    this.query = query;
  }

  public String getScript() {
    return script;
  }

  public void setScript(String script) {
    this.script = script;
  }
}
